package web.common.internal.jdo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.JdbcTransactionManager;
import web.common.core.util.StringUtil;
import web.common.internal.jdo.engine.JdoBuilder;
import web.common.internal.jdo.metadata.TableChecker;
import web.common.jdo.engine.Jdo;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @author Jin Zheng
 * @since 1.0 2022-01-12
 */
@Configuration
@EnableConfigurationProperties(JdoProperties.class)
@ConditionalOnProperty(prefix = "web.jdo", name = "enabled", havingValue = "true")
public class JdoAutoConfiguration {
	private static Logger logger = LoggerFactory.getLogger(JdoAutoConfiguration.class);
	
	public JdoAutoConfiguration() {
	}

	@Bean
	public DataSource dataSource(JdoProperties jdoProp) {
		var prop = jdoProp.getDatasource();
		var dataSource = new DruidDataSource();
		dataSource.setUrl(prop.getUrl());
		dataSource.setUsername(prop.getUsername());
		dataSource.setPassword(prop.getPassword());

		dataSource.setInitialSize(prop.getInitialSize());
		dataSource.setMinIdle(prop.getMinIdle());
		dataSource.setMaxActive(prop.getMaxActive());
		dataSource.setMaxWait(prop.getMaxWait());

		dataSource.setTimeBetweenEvictionRunsMillis(prop.getTimeBetweenEvictionRunsMillis());
		dataSource.setMinEvictableIdleTimeMillis(prop.getMinEvictableIdleTimeMillis());
		dataSource.setMaxEvictableIdleTimeMillis(prop.getMaxEvictableIdleTimeMillis());

		dataSource.setValidationQuery(prop.getValidationQuery());
		dataSource.setTestWhileIdle(prop.isTestWhileIdle());
		dataSource.setTestOnBorrow(prop.isTestOnBorrow());
		dataSource.setTestOnReturn(prop.isTestOnReturn());
		dataSource.setKeepAlive(prop.isKeepAlive());
		dataSource.setPoolPreparedStatements(prop.isPoolPreparedStatements());
		dataSource.setAsyncInit(prop.isAsyncInit());

		try {
			dataSource.setFilters(prop.getFilters());
			dataSource.init();
		} catch (SQLException e) {
			logger.error("Error, ", e);
		}
		logger.info("DruidDataSource initialize, url: {}", prop.getUrl());
		return dataSource;
	}

	@Bean
	public JdbcTemplate jdbcTemplate(DataSource ds) {
		return new JdbcTemplate(ds);
	}

	@Bean
	public Jdo jdo(JdoProperties prop, DataSource ds, JdbcTemplate jdbcTemplate) {
		var pkgs = prop.getScanPackages();
		var builder = new JdoBuilder()
				.setDataSource(ds)
				.setJdbcTemplate(jdbcTemplate)
				.setEscape(prop.isEscape())
				.addScanPackages(StringUtil.split(pkgs));
		var jdo = builder.build();
		var parserFactory = builder.getParserFactory();

		var tableChecker = new TableChecker(ds, parserFactory.getEntityConfigCache());
		tableChecker.check();

		return jdo;
	}

	@Bean
	public DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
	                                                ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
		DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
		transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
		return transactionManager;
	}

	private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
		return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
				? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource);
	}

}
